﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace InnovTech.Util.Workflow
{

    public class WorkStepStartingEventArgs : CancelEventArgs
    {
        public WorkStepStartingEventArgs(bool cancel)
            : base(cancel)
        {
        }

        public WorkStepStartingEventArgs()
            : base()
        {
        }
    }

    public class WorkStepCompletedEventArgs : EventArgs
    {
        public WorkStepCompletedEventArgs(object result)
        {
            _result = result;
        }

        private object _result;

        public object Result { get { return _result; } }
    }

    public interface IWorkStep
    {
        event EventHandler<WorkStepStartingEventArgs> Starting;
        event EventHandler<WorkStepCompletedEventArgs> Completed;
        object Id { get; }
        IWorkStep NextStep { get; set; }
        void Run();
        object Result { get; }
        void SetInput(object input);
        WorkStepState State { get; }
        Exception LastException { get; }
    }

    public enum WorkStepState
    {
        Initialized,
        Starting,
        Completed,
        Cancelled,
        Error
    }

    public class InvalidStateException : Exception
    {

        WorkStepState _state;
        public InvalidStateException(string msg, WorkStepState state)
            : base(msg)
        {
            _state = state;
        }

        public WorkStepState ErrorState
        {
            get { return _state; }
        }
    }

    public class WorkStep<TInput, TResult> : IWorkStep
    {
        Func<TInput, TResult> _logic;
        public Func<TResult, IWorkStep> GetNextStep { get; set; }

        public WorkStep(Func<TInput, TResult> logic)
        {
            _logic = logic;
            State = WorkStepState.Initialized;
            Id = Guid.NewGuid().ToString();
        }

        public event EventHandler<WorkStepStartingEventArgs> Starting;

        public event EventHandler<WorkStepCompletedEventArgs> Completed;

        public object Id
        {
            get;
            private set;
        }

        public IWorkStep NextStep
        {
            get 
            { 
                return GetNextStep == null ? null : GetNextStep(GetResult()); 
            }
            set
            {
                GetNextStep = result => value;
            }
        }

       

        public object Result
        {
            get;
            private set;
        }

        public TResult GetResult()
        {
            return (TResult)Result;
        }

        protected TInput Input { get; set; }
        public void SetInput(object input)
        {
            if(input != null)
                Input = (TInput)input;
        }

        public WorkStepState State
        {
            get;
            private set;
        }


        public Exception LastException
        {
            get;
            private set;
        }

        public void Run()
        {
            if (State != WorkStepState.Initialized)
                throw new InvalidStateException("A work step can only start with the Initialized state", State);

            State = WorkStepState.Starting;
            OnStarting();

            if (Starting != null)
            {
                WorkStepStartingEventArgs args = new WorkStepStartingEventArgs();
                Starting(this, args);
                if (args.Cancel)
                {
                    State = WorkStepState.Cancelled;
                    OnCancel();
                    return;
                }
            }

            try
            {
                Result = _logic(Input);

                if (Completed != null)
                {
                    WorkStepCompletedEventArgs cArgs = new WorkStepCompletedEventArgs(Result);
                    Completed(this, cArgs);
                }
                State = WorkStepState.Completed;
                OnCompleted();
            }
            catch (Exception ex)
            {
                State = WorkStepState.Error;
                OnError();
                LastException = ex;
            }
            
        }

        protected virtual void OnStarting()
        {
        }

        protected virtual void OnCompleted()
        {
        }

        protected virtual void OnError()
        {
        }

        protected virtual void OnCancel()
        {
        }
        
    }

    public class WorkStep : WorkStep<object, object>
    {
        public WorkStep(Action logic)
            : base(obj => { logic(); return null; })
        {
        }
    }
}
